#!/bin/sh
#
# SPDX-FileCopyrightText: 2015-2020 pancake
# SPDX-License-Identifier: LGPL-3.0-only
#

# rz-pm
# Honor user environment
[ -z "${SUDO}" ] && SUDO=sudo
[ -z "${WGET}" ] && WGET=wget

rizin -qv > /dev/null 2>&1
if [ $? != 0 ]; then
	echo "Cannot find rizin in PATH"
	exit 1
fi

# Exported Vars
MAKE=make
[ -z "${CURL}" ] && CURL=curl
gmake --help >/dev/null 2>&1
[ $? = 0 ] && MAKE=gmake
export MAKE
export GLOBAL=0
export RZPM_JOBS=4
export RZPMCACHE="$HOME/.rz-pm.cache"

# Set RZ_* Vars
eval $(rizin -H)
export RZ_VERSION

RZPM=$0
RZPMCACHE_LOADED=0
if [ -f ~/.rz-pm.cache ]; then
	. ~/.rz-pm.cache
	export LIBEXT="$LIBEXT"
	LIBDIR="$LIBDIR"
	export RZCONFIGHOME="$RZCONFIGHOME"
	export RZDATAHOME="$RZDATAHOME"
	PREFIX="$PREFIX"
	if [ -z "${LIBEXT}" -o -z "${LIBDIR}" -o -z "${RZCONFIGHOME}" -o -z "${RZDATAHOME}" -o -z "${PREFIX}" ]; then
		echo "[rz-pm] corrupted cache, please run rz-pm cache"
	else
		RZPMCACHE_LOADED=1
	fi
fi
if [ "${RZPMCACHE_LOADED}" != 1 ]; then
	export LIBEXT="$RZ_LIBEXT"
	LIBDIR="$RZ_LIBDIR"
	export RZCONFIGHOME="$RZ_RCONFIGHOME"
	export RZDATAHOME="$RZ_RDATAHOME"
	PREFIX="$RZ_PREFIX"
fi
BINDIR="${PREFIX}/bin/"
WRKDIR="$PWD"

# prefix
export RZPM_SYSPREFIX="${PREFIX}"
export RZPM_HOMEPREFIX="${RZDATAHOME}"
export RZPM_PREFIX="${RZPM_HOMEPREFIX}"

# bindir
export RZPM_HOMEBINDIR="${RZPM_HOMEPREFIX}/bin"
export RZPM_SYSBINDIR="${BINDIR}"

[ -z "${RZPM_BINDIR}" ] && RZPM_BINDIR=${RZPM_HOMEBINDIR}
export RZPM_BINDIR

# libdir
export RZPM_SYSLIBDIR="${LIBDIR}"

# plugdir
export RZPM_SYSPLUGIN_DIR="$RZ_LIBR_PLUGINS"
export RZPM_USRPLUGIN_DIR="$RZ_USER_PLUGINS"
[ -z "${RZPM_PLUGDIR}" ] && RZPM_PLUGDIR="${RZPM_USRPLUGIN_DIR}"
[ -z "${RZPM_PLUGDIR}" ] && RZPM_PLUGDIR="${RZDATAHOME}/plugins"

# www
export RZPM_SYSWWWROOT="`rizin -qc 'e http.root' --`"
export RZPM_HOMEWWWROOT="${RZDATAHOME}/www/"
export RZPM_WWWROOT="${RZPM_HOMEWWWROOT}"

# pkgconfig
export PKG_CONFIG_PATH="${RZPM_HOMEPREFIX}/lib/pkgconfig:${RZPM_SYSLIBDIR}/pkgconfig:${PKG_CONFIG_PATH}"
export CFLAGS="-I${RZPM_HOMEPREFIX}/include"
export LDFLAGS="-L${RZPM_HOMEPREFIX}/lib"

export RZPM_PYPATH="${RZPM_PREFIX}/python"
export RZPM_OLDPWD="${PWD}"

export RHOMEDIR="$RZ_RCONFIGHOME"

if [ "`uname`" = Darwin ]; then
	export LD_LIBRARY_PATH="${RZPM_HOMEPREFIX}/lib"
else
	export DYLD_LIBRARY_PATH="${RZPM_HOMEPREFIX}/lib"
fi

# Anything defined here will cause master not to be checked out or pulled
export RZPM_GITSKIP="${RZPM_GITSKIP}"

# Global Vars
IS_SYSPKG=0
[ -z "$RZPM_USRDIR" ] && RZPM_USRDIR="${RZDATAHOME}/rz-pm"
RZPM_ETCD="${RZCONFIGHOME}/rizinrc.d"

# TODO. support system plugin installs RZPM_PLUGDIR="${RZPM_SYSLIBDIR}/rizin/last"
if [ -z "${RZPM_GITDIR}" ]; then
	RZPM_GITDIR="${RZPM_USRDIR}/git"
fi
if [ -z "${RZPM_DBDIR}" ]; then
	if [ -d "${RZDATAHOME}/rz-pm/db" ]; then
		RZPM_DBDIR="${RZDATAHOME}/rz-pm/db"
	fi
fi
if [ -z "${RZPM_DBDIR}" ]; then
	if [ -d "${PREFIX}/share/rizin/last/rz-pm" ]; then
		RZPM_DBDIR="${PREFIX}/share/rizin/last/rz-pm/"
	fi
fi
if [ -f "d/baleful" ]; then
	RZPM_DBDIR="$(pwd)/d/"
fi

confirm() {
	printf "$@ (y/N)? "
	read A
	if [ "$A" = y -o "$A" = Y ]; then
		return 0
	fi
	return 1
}

countDown() {
	M="$1"
	C="$2"
	printf "$M"
	while : ; do
		printf "$C "
		C=$(($C-1))
		[ "$C" = 0 ] && break
	done
	echo 'Go!'
}

case "$1" in
init|up|update)
	[ -z "$RZPM_DBDIR" ] && RZPM_DBDIR="${RZPM_USRDIR}/db"
	mkdir -p "${RZPM_GITDIR}"
	mkdir -p "${HOME}/.config"
	cd "${RZPM_GITDIR}"
	if [ -d rizin-pm ]; then
		cd rizin-pm
		git reset --hard @^^
		git pull
	else
		echo git clone https://github.com/rizinorg/rizin-pm
		git clone --depth=3 --recursive https://github.com/rizinorg/rizin-pm
		cd rizin-pm
		pwd
	fi
	for a in "${RZPM_DBDIR}" "${RZPM_USRDIR}"/db2/*/db ; do
		if [ -d "$a" ]; then
			echo "Updating $a ..."
			( cd $a ; git pull )
		fi
	done

	if [ -d "${RZPM_DBDIR}" ] && [ ! -L "${RZPM_DBDIR}" ]; then
		# that's weird, it should be a symlink
		rm -f "${RZPM_DBDIR}/"*
		rmdir "${RZPM_DBDIR}"
	else
		# doesn't exist, is a file or symlink
		rm -f "${RZPM_DBDIR}"
	fi

	ln -fs "${RZPM_GITDIR}/rizin-pm/db" "${RZPM_DBDIR}"
	if [ "$1" = "init" ]; then
		echo "rz-pm database initialized. Use 'rz-pm update' to update later today."
	fi
	exit 0
	;;
esac
if [ ! -d "${RZPM_DBDIR}" ]; then
	echo '$RZPM_DBDIR: No such file or directory.'
	echo "Run 'rz-pm init' to initialize the package repository"
	exit 1
fi

mkdir -p "${RZPM_USRDIR}/pkg/"
mkdir -p "${RZPM_GITDIR}"
mkdir -p "${RZPM_PLUGDIR}"
mkdir -p "${RZPM_BINDIR}"

RZPM_SYSPKG() {
	IS_SYSPKG="$1"
	echo "RZPM_SYSPKG is going to be deprecated" >&2
}

RZPM_DESC() {
	:
}

RZPM_SUDO() {
	if [ "${GLOBAL}" = 1 ]; then
		${SUDO} "$@"
	else
		"$@"
	fi
}
export RZPM_SUDO="RZPM_SUDO"

RZPM_List() {
	cd "${RZPM_USRDIR}/pkg"
	if [ -n "$1" ]; then
		ls | grep -i "$1"
	else
		ls | cat
	fi
}

RZPM_Search() {
	#helper for pretty printing tabbed lists
	if ! type "column" > /dev/null; then
		cols() { cat ; }
	else
		cols() { column -s `printf "\t"` -t; }
	fi
	for a in "${RZPM_DBDIR}" "${RZPM_USRDIR}"/db2/*/db ; do
		[ -d "$a" ] || continue
(
		cd "${a}"
		if [ -n "$1" ]; then
			grep RZPM_DESC * /dev/null | sed -e "s,:RZPM_DESC,    `printf " \t"`," -e 's,",,g' | \
				grep -i "$1" | cols
		else
			grep RZPM_DESC * /dev/null | sed -e "s,:RZPM_DESC,    `printf " \t"`," -e 's,",,g' | cols
		fi
)
	done
}

RZPM_TGZ() {
	URL="$1"
	TARBALL="`basename $1`"
	if [ -z "$2" ]; then
		DIR="`echo ${TARBALL} | awk -F .t '{print $1}'`"
	elif [ "${2%${2#?}}"x = '/x' ]; then
		DIR="${2#?}"
	else
		DIR="$2"
	fi
	if [ -n "$3" ]; then
		TARBALL="$3"
	fi
	cd "${RZPM_GITDIR}"
	if [ "$ACTION" = clean ]; then
		rm -f "${TARBALL}"
		exit 0
	fi
	${WGET} -O ${TARBALL} -c "${URL}"
	if [ -d "${DIR}" ]; then
		echo "Already uncompressed."
	else
		if [ "`echo ${URL} | grep -e tgz -e tar.gz`" ]; then
			tar xzvf "${TARBALL}"
		elif [ "`echo ${URL} | grep -e .tbz2 -e tar.bz2`" ]; then
			tar xjvf "${TARBALL}"
		elif [ "`echo ${URL} | grep -e .txz -e tar.xz`" ]; then
			tar xJvf "${TARBALL}"
		elif [ "`echo ${URL} | grep -e .txz -e tar`" ]; then
			tar xvf "${TARBALL}"
		elif [ "`echo ${URL} | grep -e .zip`" ]; then
			if [ -n "$2" ] && [ "${2%${2#?}}"x != '/x' ]; then
				unzip -d "${DIR}" -o "${TARBALL}"
			else
				unzip "${TARBALL}"
			fi
		else
			echo "Dunno"
			exit 1
		fi
	fi
	cd "${DIR}" || RZPM_FAIL "Oops"
}

RZPM_DOC() {
	RZPM_DOC="$@"
}

RZPM_GIT() {
	[ -z "${NAME}" -o "$ACTION" = clean ] && return
	URL="$1"
	if [ -z "$2" ]; then
		DIR="`basename $1`"
	else
		DIR="$2"
	fi
	cd "${RZPM_GITDIR}"
	if [ -d "${DIR}" ]; then
		cd "${DIR}"
		if [ -z "${RZPM_GITSKIP}" ]; then
			git pull
		fi
	else
		git clone --recursive "${URL}" "${DIR}" || RZPM_FAIL "Oops"
		cd "${DIR}" || RZPM_FAIL "Oops"
	fi
	rizin -qv | grep -q git || { # rizin >= 4.5
		if [ -z "${RZPM_GITSKIP}" ]; then
			git checkout "rz-${RZ_VERSION}"
			git pull
		fi
	}
}

RZPM_SVN() {
	URL="$1"
	if [ -z "$2" ]; then
		DIR="${NAME}"
	else
		DIR="$2"
	fi
	cd "${RZPM_GITDIR}" || exit 1
	if [ -d "${DIR}" ]; then
		cd "${DIR}"
		svn up
	else
		svn co "${URL}" "${DIR}"
		cd "${DIR}" || RZPM_FAIL "Oops"
	fi
}

RZPM_FAIL() {
	printf "\033[31mERROR: \033[32m$@\033[0m\n"
	exit 1
}

RZPM_BEGIN() {
	RZPM_GIT=""
	RZPM_DEPS=""
}

RZPM_Info() {
	if [ -n "${RZPM_DBDIR}" -a -d "${RZPM_DBDIR}" ]; then
		echo "# Repository Database:"
		cd "${RZPM_DBDIR}"
		printf "# $(ls |wc -l) Packages\n" >&2
	fi
	cd "${RZPM_USRDIR}/pkg"
	echo "# Installed:"
	printf "# %s Packages\n# " "$(ls | wc -l)" >&2
	du -hs "${HOME}/.config/rizin" >&2
}

RZPM_REGISTER() {
	date > "${RZPM_USRDIR}/pkg/$NAME"
}

RZPM_UNREGISTER() {
	rm -f "${RZPM_USRDIR}/pkg/$NAME"
}

RZPM_END() {
	if [ -n "${NAME}" ]; then
		echo "${ACTION} Done For ${NAME}"
	fi
}

RZPM_DEPS() {
	if [ -n "${RZPM_IGNORE_DEPS}" ]; then
		return
	fi
	for a in "$@" ; do
		echo "DEPENDS: $a"
		rz-pm -w "$a"
		doInstall=$?
		if [ "${RZPM_UPGRADE}" ]; then
			doInstall=1
		fi
		if [ $doInstall = 1 ]; then
			rz-pm -i "$a"
		fi
	done
}

pkgFilePath() {
	for a in "${RZPM_DBDIR}" "${RZPM_USRDIR}"/db2/*/db ; do
		if [ -f "$a/$1" ]; then
			echo "$a/$1"
			return
		fi
	done
}

rz_pm_doc() {
	if [ -z "$2" ]; then
		echo "Usage: rz-pm -d [package]  # show docs on [package]"
		exit 1
	else
		FILE="$(pkgFilePath "$1")"
		if [ -f "${FILE}" ]; then
			. "${FILE}"
			echo "${RZPM_DOC}"
		fi
	fi
}

rz_pm_install() {
	FILE="$(pkgFilePath "$2")"
	if [ -f "${FILE}" ]; then
		NAME="$2"
		ACTION=Install
		export PATH="${RZPM_HOMEBINDIR}:${PATH}"
		. "${FILE}"
		RZPM_INSTALL
		RZPM_REGISTER
	else
		echo "Cannot find $2"
		exit 1
	fi
}

rz_pm_uninstall() {
	if [ -z "$2" ]; then
		echo "Usage: rz-pm -u [package]  # uninstall [package]"
		exit 1
	else
		FILE="`pkgFilePath $2`"
		if [ -f "${FILE}" ]; then
			NAME="$2"
			ACTION=Uninstall
			. "${FILE}"
			RZPM_UNINSTALL
			if [ $? = 0 ]; then
				RZPM_UNREGISTER
			else
				echo Oops
			fi
		else
			echo "Unable to find package template for $2." >&2
			echo "Use -uu to remove it from the database." >&2
		fi
	fi
}

export PYTHON_PATH="${RZPM_PYPATH}:${PYTHON_PATH}"

case "$1" in
-I|info)
	RZPM_Info
	;;
-i|install)
	if [ -z "$2" ]; then
		echo "Usage: rz-pm -i [package]  # install [package]"
		exit 1
	elif [ "$2" = "all" ]; then
		for a in `cd ${RZPM_DBDIR} && ls` ; do
			rz_pm_install "$a"
			if [ "$?" != 0 ]; then
				printf "\033[31mERROR: Installation failed for $a\033[0m\n"
			fi
		done
	else
		A1="$1"
		while [ -n "$2" ] ; do
			rz_pm_install $A1 "$2"
			shift
		done
	fi
	;;
-uu)
	FILE="${RZPM_USRDIR}/pkg/$2"
	$0 -u $2 2> /dev/null
	rm -f "${FILE}"
	;;
-u|uninstall)
	rz_pm_uninstall "$@"
	;;
-l|list)
	RZPM_List "$2"
	;;
-s|search)
	RZPM_Search "$2"
	;;
-r|run)
	shift
	PATH="${RZPM_BINDIR}:${PATH}" "$@"
	;;
-v|version)
	echo "rz-pm ${RZ_VERSION}"
	;;
-gi|global-install)
	GLOBAL=1
	RZPM_PREFIX="${RZPM_SYSPREFIX}"
	#RZPM_PLUGDIR="${RZPM_SYSLIBDIR}/rizin/${RZ_VERSION}/"
	RZPM_PLUGDIR="${RZPM_SYSPLUGIN_DIR}"
	RZPM_BINDIR="${RZPM_SYSBINDIR}"
	RZPM_WWWROOT="${RZPM_SYSWWWROOT}"
	#TODO set RZPM_ETCD= to a "global" rizinrc.d
	if [ ! -d "${RZPM_PLUGDIR}" ]; then
		mkdir -p "${RZPM_PLUGDIR}"
	fi
	rz_pm_install "$@"
	;;
-gu|global-uninstall)
	GLOBAL=1
	RZPM_PREFIX="${RZPM_SYSPREFIX}"
	#RZPM_PLUGDIR="${RZPM_SYSLIBDIR}/rizin/${RZ_VERSION}/"
	RZPM_PLUGDIR="${RZPM_SYSPLUGIN_DIR}"
	RZPM_BINDIR="${RZPM_SYSBINDIR}"
	RZPM_WWWROOT="${RZPM_SYSWWWROOT}"
	#TODO set RZPM_ETCD= to a "global" rizinrc.d
	rz_pm_uninstall "$@"
	;;
-cp)
	if [ -n "${RZPM_PLUGDIR}" ]; then
		rm -f "${RZPM_PLUGDIR}/*"
	fi
	;;
-ci)
	shift
	${RZPM} clean $*
	${RZPM} install $*
	;;
-c|clean)
	if [ -n "$2" ]; then
		RC=0
		while [ -n "$2" ]; do
			echo "Cleaning $2..."
			FILE="$(pkgFilePath "$2")"
			if [ -f "${FILE}" ]; then
				NAME="$2"
				ACTION=clean
				export PATH="${RZPM_HOMEBINDIR}:${PATH}"
				. "${FILE}"
				echo $FILE TGZ=$RZPM_TGZ
			else
				echo "Cannot find $FILE" >&2
				RC=1
			fi

			if [ -d "${RZPM_GITDIR}/$2" ]; then
				echo "Cleaning up ${RZPM_GITDIR}/$2..."
				rm -rf "${RZPM_GITDIR}/$2"
			else
				echo "Cannot find $2 in ${RZPM_GITDIR}" >&2
				RC=1
			fi
			shift
		done
		exit $RC
	else
		RZPM_List rizin | grep rizin
		if [ $? != 0 ]; then
			echo "Press enter to remove the GITDIR or ^C to cancel operation"
			read A
			rm -rf "${RZPM_GITDIR}"
			mkdir -p "${RZPM_GITDIR}"
		else
			echo "rizin is installed with rz-pm, do not clean the cache to avoid autodestruction"
			echo "do not disassemble do not disassemble do not disassemble do not disassemble"
			exit 1
		fi
	fi
	;;
what|-w)
	if [ -n "$2" ]; then
		exec cat "${RZPM_USRDIR}/pkg/$2" 2> /dev/null
	else
		cd "${RZPM_USRDIR}/pkg/" 2> /dev/null
		ls
	fi
	;;
cd)
	if [ -n "$2" ]; then
		cd "${RZPM_GITDIR}/$2"
		export RZPM_LEVEL=$((${RZPM_LEVEL}+1))
		echo "Entering into ${SHELL} level ${RZPM_LEVEL}."
		PS1="rz-pm:${RZPM_LEVEL}:\W\$ " ${SHELL}
		echo "Exiting level ${RZPM_LEVEL}."
		exit 0
	else
		echo "Missing argument. See rz-pm ls"
		exit 1
	fi
	;;
ls)
	if [ -d "${RZPM_GITDIR}" ]; then
		ls "${RZPM_GITDIR}"
	else
		echo "Cannot find ${RZPM_GITDIR}"
		exit 1
	fi
	;;
cache)
	echo "export LIBEXT=\"${LIBEXT}\"" > "${RZPMCACHE}"
	echo "export LIBDIR=\"${LIBDIR}\"" >> "${RZPMCACHE}"
	echo "export RZCONFIGHOME=\"${RZCONFIGHOME}\"" >> "${RZPMCACHE}"
	echo "export RZDATAHOME=\"${RZDATAHOME}\"" >> "${RZPMCACHE}"
	echo "export PREFIX=\"${PREFIX}\"" >> "${RZPMCACHE}"
	echo "[rz-pm] rizin environment cached in ${RZPMCACHE}"
	;;
-a|add)
	if [ "$2" = "help" ]; then
		printf '%s\n' \
			"Usage: rz-pm -a [url] ([name]) # manage package repositories" \
			" $ rz-pm -a https://github.com/foo/bar  # register bar repo" \
			" $ rz-pm -a                             # list all repos" \
			" $ rz-pm -a - bar                       # delete 'foo' repo"
		exit 1
	fi
	cd "${RZPM_DBDIR}"
	cd ..
	mkdir -p db2
	cd db2
	URL="$2"
	NAME="$3"
	if [ -n "$URL" -a -z "$NAME" ]; then
		NAME=`basename $URL`
	fi
	if [ -n "$URL" -a -n "$NAME" ]; then
		if [ "$URL" = "-" ]; then
			if [ -d "$NAME" ]; then
				rm -rf "$NAME"
				echo "Unregistered repository"
			else
				echo "Cannot find '$NAME'"
				exit 1
			fi
		else
			echo "Registering this db repository"
			if [ -d "$URL" ]; then
				cd "$URL"
				git pull
			else
				git clone --recursive --depth=2 "$URL" "$NAME"
			fi
			if [ ! -d "$NAME/db" ]; then
				echo "This is not an rz-pm repository"
				rm -rf "$NAME"
				exit 1
			fi
		fi
	else
		ls
	fi
	cd "$WRKDIR"
	;;
-H)
	if [ -z "$2" ]; then
		echo "Usage: rz-pm -H [VARNAME]"
		echo "Variables: RZPM_PLUGDIR RZPM_BINDIR RZPM_DBDIR RZPM_GITDIR"
		exit 1
	fi
	eval echo "\$$2"
	;;
-d|doc|--doc)
	rz_pm_doc "$2"
	;;
*|help|-h)
	cat <<HELP
Usage: rz-pm [init|update|cmd] [...]
Commands:
 -I,info                     information about repository and installed packages
 -i,install <pkgname>        install or update package in your home (pkgname=all)
 -gi,global-install <pkg>    install or update package system-wide
 -gu,global-uninstall <pkg>  uninstall pkg from systemdir
 -u,uninstall <pkgname>      rz-pm -u baleful (-uu to force)
 -l,list                     list installed pkgs
 -r,run [cmd ...args]        run shell command with RZPM_BINDIR in PATH
 -s,search [<keyword>]       search in database
 -v,version                  show version
 -h,help                     show this message
 -H variable                 show value of given variable
 -c,clean ([git/dir])        clear source cache (GITDIR)
 -ci (pkgname)               clean install of given package
 -cp                         clean the user's home plugin directory
 -d,doc [pkgname]            show documentation for given package
 -w <pkgname>                what/where is installed
 init | update ..            initialize/update database
 cd [git/dir]                cd into given git (see 'rz-pm ls')
 ls                          ls all cloned git repos in GITDIR
 cache                       cache contents of rizin -H to make rz-pm rizin-independent
Environment:
 SUDO=sudo                    use this tool as sudo
 RZPM_PLUGDIR=${RZPM_PLUGDIR}
 RZPM_BINDIR=${RZPM_BINDIR}
 RZPM_DBDIR=${RZPM_DBDIR}
 RZPM_GITDIR=${RZPM_GITDIR}
 RZPM_GITSKIP=${RZPM_GITSKIP}
HELP
	;;
esac
